#region Copyright & License
// Copyright 2008-2009 BUGCOLLECT.COM
// Author: Remus Rusanu
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//                 http://bugcollect.com/terms
//
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Diagnostics;
using System.IO;
using System.Xml.XPath;

namespace bugcollect.open
{

    /// <summary>
    /// This class represents the report submission completion callback arguments.
    /// It aggregates together the callback user state, the submission result and
    /// any exception that may occured during submission.
    /// </summary>
    /// <remarks>If an exception occured during submission then the Exception property
    /// is set with a reference to that exception. </remarks>
    public class ReportSubmissionResultArgs : EventArgs
    {
        private object _userState;
        private Result _result;
        private Exception _exception;

        /// <summary>
        /// The user state object that was provided in the call to Submit or SubmitReport.
        /// </summary>
        /// <seealso cref="ReportingManager.Submit"/>
        /// <seealso cref="ReportingManager.SubmitReport"/>
        public object UserState
        {
            get { return _userState; }
        }

        /// <summary>
        /// The result of the report submission.
        /// </summary>
        public Result Result
        {
            get { return _result; }
        }

        /// <summary>
        /// Any exception that may have occured during the report submission.
        /// If this property is set then the submission result is undetermined.
        /// </summary>
        public Exception Exception
        {
            get { return _exception; }
        }

        internal ReportSubmissionResultArgs(
            object userState,
            Result result,
            Exception exception)
        {
            _userState = userState;
            _result = result;
            _exception = exception;
        }
    }

    /// <summary>
    /// The callback delegate for report submission completion.
    /// </summary>
    /// <remarks>
    /// You must provide a callback if you need to know the result of a report submission.
    /// The <a href="T_bugcollect.open_ReportingManager.htm">ReportingManager</a> will invoke this callback  
    /// exactly once with the report submission response result, or with the exception occured
    /// if the report submission failed.
    /// </remarks>
    /// <seealso cref="ReportingManager.Submit"/>
    /// <seealso cref="ReportingManager.SubmitReport"/>
    /// <param name="sender">The ReportingManager that is invoking the callback.</param>
    /// <param name="args">The report submission result arguments.</param>
    public delegate void ReportSubmissionResultHandler(
        object sender,
        ReportSubmissionResultArgs args);



    /// <summary>
    /// The <a href="http://bugcollect.com">bugcollect.com</a> response result status for the report.
    /// </summary>
    /// <seealso cref="bugcollect.Result"/>
    public enum EResultStatus
    {
        /// <summary>
        /// Unitialized value.
        /// </summary>
        NONE,
        /// <summary>
        /// The account is currently not subscribed. No reports are accepted.
        /// </summary>
        UNSUBSCRIBED,
        /// <summary>
        /// The report was accepted and a new record was created.
        /// </summary>
        CREATED,
        /// <summary>
        /// The report was accepted but the bucket is marked to ignore new reports.
        /// </summary>
        IGNORED,
        /// <summary>
        /// An error occured during submission.
        /// </summary>
        ERROR,
    }

    /// <summary>
    /// This class stores the <a href="http://bugcollect.com">bugcollect.com</a> response result of a report submission. 
    /// <seealso cref="bugcollect.ReportSubmissionResultHandler"/>
    /// </summary>
    public class Result
    {
        private const string tagNamespace = "http://bugcollect.com/schemas/result/v1.0";

        private EResultStatus _status = EResultStatus.NONE;

        /// <summary>
        /// The response status.
        /// </summary>
        public EResultStatus Status
        {
            get { return _status; }
        }

        private Stream _response;
        /// <summary>
        /// The response stream. This contains the response
        /// set for the reported incident bucket at <a href="http://bugcollect.com">bugcollect.com</a>.
        /// </summary>
        /// <remarks>For example the bucket can be marked at <a href="http://bugcollect.com">bugcollect.com</a> to return a response that
        /// directs the end user to download a newer version of the application. The application should
        /// parse the response and display the appropiate message to the end user.</remarks>
        public Stream Response
        {
            get { return _response; }
        }

        private Int32 _reportId;

        /// <summary>
        /// The ID assigned to the report by bugcollect.com.
        /// </summary>
        public Int32 ReportId
        {
            get { return _reportId; }
        }

        private Int32 _bucketId;

        /// <summary>
        /// The ID assigned to the bucket into which the report was categorized.
        /// </summary>
        public Int32 BucketId
        {
            get { return _bucketId; }
        }

        private Int32 _sourceId;

        /// <summary>
        /// The ID assigned to the source which submitted the report.
        /// </summary>
        public Int32 SourceId
        {
            get { return _sourceId; }
        }

        private Guid _reportUuid;

        /// <summary>
        /// The report UUID.
        /// </summary>
        /// <remarks>
        /// If the submitted report did not have an UUID, bugcollect.com will
        /// generate one and return it in the result.
        /// </remarks>
        /// <seealso cref="Report.UUID"/>
        public Guid ReportUuid
        {
            get { return _reportUuid; }
        }

        internal static Result CreateSuppressedResult(byte[] response)
        {
            Result result = new Result();
            result._status = EResultStatus.IGNORED;
            result._response = new MemoryStream(response);
            return result;
        }

        internal static bool ParseXML(
            XmlReader reader,
            out Result result)
        {
            XPathDocument xnavDoc = new XPathDocument(reader);
            XPathNavigator xnav = xnavDoc.CreateNavigator();

            XmlNamespaceManager mgr = new XmlNamespaceManager(reader.NameTable);
            mgr.AddNamespace("r", tagNamespace);

            Result res = new Result();
            foreach (XPathNavigator xnavNode in xnav.Select("/r:result/r:status", mgr))
            {
                res._status = (EResultStatus)Enum.Parse(typeof(EResultStatus), xnavNode.Value);
            }
            foreach (XPathNavigator xnavNode in xnav.Select("/r:result/r:response", mgr))
            {
                res._response = new MemoryStream(Convert.FromBase64String(xnavNode.Value));
            }
            foreach (XPathNavigator xnavNode in xnav.Select("/r:result/r:report_id", mgr))
            {
                res._reportId = Convert.ToInt32(xnavNode.Value);
            }
            foreach (XPathNavigator xnavNode in xnav.Select("/r:result/r:bucket_id", mgr))
            {
                res._bucketId = Convert.ToInt32(xnavNode.Value);
            }
            foreach (XPathNavigator xnavNode in xnav.Select("/r:result/r:source_id", mgr))
            {
                res._sourceId = Convert.ToInt32(xnavNode.Value);
            }
            foreach (XPathNavigator xnavNode in xnav.Select("/r:result/r:report_uuid", mgr))
            {
                res._reportUuid = new Guid(xnavNode.Value);
            }
            result = res;
            return true;
        }
    }
}
